package com.yc.cloud.admin.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * FFmpeg 工具类
 * 用于视频处理，包括截取视频帧、获取视频信息等
 * 
 * @author kinggu
 * @since 2025-01-XX
 */
@Slf4j
@Component
public class FFmpegUtils {

    /**
     * FFmpeg 可执行文件路径
     */
    @Value("${custom.ffmpeg.bin:ffmpeg}")
    private String ffmpegBin;

    /**
     * 默认输出目录
     */
    @Value("${custom.ffmpeg.output-dir:temp/video_thumbnails}")
    private String defaultOutputDir;

    /**
     * 从流地址截取一帧保存为图片
     * 
     * @param streamUrl 流地址（支持 RTSP、RTMP、HTTP 等）
     * @param outputPath 输出文件路径
     * @return 截图文件路径，失败返回null
     */
    public String captureFrameFromStream(String streamUrl, String outputPath) {
        return captureFrameFromStream(streamUrl, outputPath, null, null, 2, 10);
    }

    /**
     * 从流地址截取一帧保存为图片（完整参数）
     * 
     * @param streamUrl 流地址（支持 RTSP、RTMP、HTTP 等）
     * @param outputPath 输出文件路径
     * @param width 输出宽度，为null时保持原始比例
     * @param height 输出高度，为null时保持原始比例
     * @param quality 输出质量 (1-31, 数值越小质量越高)
     * @param timeoutSeconds 超时时间（秒）
     * @return 截图文件路径，失败返回null
     */
    public String captureFrameFromStream(String streamUrl, String outputPath, Integer width, Integer height, Integer quality, int timeoutSeconds) {
        try {
            if (streamUrl == null || streamUrl.trim().isEmpty()) {
                log.error("流地址为空");
                return null;
            }

            // 确保输出目录存在
            File outputFile = new File(outputPath);
            File outputDir = outputFile.getParentFile();
            if (outputDir != null && !outputDir.exists()) {
                outputDir.mkdirs();
            }

            // 构建FFmpeg命令 - 从流截图
            List<String> command = buildCaptureFromStreamCommand(streamUrl, outputPath, width, height, quality);
            
            log.info("执行FFmpeg从流截图命令: {}", String.join(" ", command));

            // 执行命令
            ProcessBuilder pb = new ProcessBuilder(command);
            pb.redirectErrorStream(true);
            Process process = pb.start();

            // 读取输出
            StringBuilder output = new StringBuilder();
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    output.append(line).append("\n");
                }
            }

            // 等待命令执行完成
            boolean finished = process.waitFor(timeoutSeconds, TimeUnit.SECONDS);
            if (!finished) {
                process.destroyForcibly();
                log.error("FFmpeg从流截图超时: streamUrl={}, timeout={}s", streamUrl, timeoutSeconds);
                return null;
            }

            int exitCode = process.exitValue();
            if (exitCode == 0) {
                // 验证输出文件是否生成
                if (outputFile.exists() && outputFile.length() > 0) {
                    log.info("从流截图成功: {} -> {}, 文件大小: {} bytes", streamUrl, outputPath, outputFile.length());
                    return outputPath;
                } else {
                    log.error("FFmpeg执行成功但未生成有效输出文件: {}", outputPath);
                    return null;
                }
            } else {
                log.error("FFmpeg从流截图失败，退出码: {}, streamUrl: {}, 输出: {}", exitCode, streamUrl, output.toString());
                return null;
            }

        } catch (Exception e) {
            log.error("从流截图异常: streamUrl={}", streamUrl, e);
            return null;
        }
    }

    /**
     * 构建从流截图的FFmpeg命令
     */
    private List<String> buildCaptureFromStreamCommand(String streamUrl, String outputPath, Integer width, Integer height, Integer quality) {
        List<String> command = new ArrayList<>();
        command.add(ffmpegBin);
        
        String lowerUrl = streamUrl.toLowerCase();
        
        // 输入选项 - 针对不同流类型优化
        if (lowerUrl.startsWith("rtsp://")) {
            // RTSP 优化参数
            command.add("-rtsp_transport");
            command.add("tcp"); // 使用TCP传输，更稳定
            command.add("-timeout");
            command.add("5000000"); // 5秒超时（微秒）
        } else if (lowerUrl.startsWith("rtmp://")) {
            // RTMP 优化参数
            command.add("-fflags");
            command.add("nobuffer"); // 减少缓冲，加快读取
            command.add("-analyzeduration");
            command.add("1000000"); // 1秒分析时长（微秒）
            command.add("-probesize");
            command.add("1000000"); // 1MB探测大小
        } else {
            // HTTP/HLS 等其他流
            command.add("-timeout");
            command.add("5000000"); // 5秒超时（微秒）
        }
        
        // 输入流地址
        command.add("-i");
        command.add(streamUrl);
        
        // 只截取一帧
        command.add("-vframes");
        command.add("1");
        
        // 分辨率设置
        if (width != null && height != null) {
            command.add("-vf");
            command.add(String.format("scale=%d:%d", width, height));
        } else if (width != null) {
            command.add("-vf");
            command.add(String.format("scale=%d:-1", width));
        } else if (height != null) {
            command.add("-vf");
            command.add(String.format("scale=-1:%d", height));
        }
        
        // 输出质量
        command.add("-q:v");
        command.add(String.valueOf(quality != null ? quality : 2));
        
        // 覆盖已存在的文件
        command.add("-y");
        
        // 输出文件
        command.add(outputPath);
        
        return command;
    }

    /**
     * 从视频文件截取一帧作为封面图
     * 
     * @param videoPath 视频文件路径
     * @return 截图文件路径，失败返回null
     */
    public String captureFrame(String videoPath) {
        return captureFrame(videoPath, null, null, 1.0);
    }

    /**
     * 从视频文件截取一帧作为封面图
     * 
     * @param videoPath 视频文件路径
     * @param outputPath 输出文件路径，为null时自动生成
     * @return 截图文件路径，失败返回null
     */
    public String captureFrame(String videoPath, String outputPath) {
        return captureFrame(videoPath, outputPath, null, 1.0);
    }

    /**
     * 从视频文件截取一帧作为封面图
     * 
     * @param videoPath 视频文件路径
     * @param outputPath 输出文件路径，为null时自动生成
     * @param timePosition 截取时间位置（秒），为null时自动选择视频中间位置
     * @return 截图文件路径，失败返回null
     */
    public String captureFrame(String videoPath, String outputPath, Double timePosition) {
        return captureFrame(videoPath, outputPath, timePosition, 1.0);
    }

    /**
     * 从视频文件截取一帧作为封面图（指定分辨率）
     * 
     * @param videoPath 视频文件路径
     * @param outputPath 输出文件路径，为null时自动生成
     * @param timePosition 截取时间位置（秒），为null时自动选择视频中间位置
     * @param width 输出宽度，为null时保持原始比例
     * @param height 输出高度，为null时保持原始比例
     * @return 截图文件路径，失败返回null
     */
    public String captureFrame(String videoPath, String outputPath, Double timePosition, Integer width, Integer height) {
        return captureFrame(videoPath, outputPath, timePosition, width, height, 2);
    }

    /**
     * 从视频文件截取一帧作为封面图（指定分辨率和质量）
     * 
     * @param videoPath 视频文件路径
     * @param outputPath 输出文件路径，为null时自动生成
     * @param timePosition 截取时间位置（秒），为null时自动选择视频中间位置
     * @param width 输出宽度，为null时保持原始比例
     * @param height 输出高度，为null时保持原始比例
     * @param quality 输出质量 (1-31, 数值越小质量越高)
     * @return 截图文件路径，失败返回null
     */
    public String captureFrame(String videoPath, String outputPath, Double timePosition, Integer width, Integer height, Integer quality) {
        try {
            // 验证输入文件
            File videoFile = new File(videoPath);
            if (!videoFile.exists() || !videoFile.isFile()) {
                log.error("视频文件不存在: {}", videoPath);
                return null;
            }

            // 生成输出路径
            if (outputPath == null) {
                outputPath = generateOutputPath(videoPath);
            }

            // 确保输出目录存在
            File outputFile = new File(outputPath);
            File outputDir = outputFile.getParentFile();
            if (!outputDir.exists()) {
                outputDir.mkdirs();
            }

            // 获取视频时长，用于计算截取位置
            if (timePosition == null) {
                Double duration = getVideoDuration(videoPath);
                if (duration != null && duration > 0) {
                    // 选择视频中间位置，但不超过30秒（避免片头片尾）
                    timePosition = Math.min(duration / 2, 30.0);
                } else {
                    timePosition = 1.0; // 默认第1秒
                }
            }

            // 构建FFmpeg命令
            List<String> command = buildCaptureCommandWithResolution(videoPath, outputPath, timePosition, width, height, quality);
            
            log.info("执行FFmpeg截图命令: {}", String.join(" ", command));

            // 执行命令
            ProcessBuilder pb = new ProcessBuilder(command);
            pb.redirectErrorStream(true);
            Process process = pb.start();

            // 读取输出
            StringBuilder output = new StringBuilder();
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    output.append(line).append("\n");
                }
            }

            // 等待命令执行完成
            boolean finished = process.waitFor(30, TimeUnit.SECONDS);
            if (!finished) {
                process.destroyForcibly();
                log.error("FFmpeg命令执行超时: {}", videoPath);
                return null;
            }

            int exitCode = process.exitValue();
            if (exitCode == 0) {
                // 验证输出文件是否生成
                if (new File(outputPath).exists()) {
                    log.info("视频截图成功: {} -> {}", videoPath, outputPath);
                    return outputPath;
                } else {
                    log.error("FFmpeg执行成功但未生成输出文件: {}", outputPath);
                    return null;
                }
            } else {
                log.error("FFmpeg命令执行失败，退出码: {}, 输出: {}", exitCode, output.toString());
                return null;
            }

        } catch (Exception e) {
            log.error("视频截图异常: videoPath={}", videoPath, e);
            return null;
        }
    }

    /**
     * 从视频文件截取一帧作为封面图
     * 
     * @param videoPath 视频文件路径
     * @param outputPath 输出文件路径，为null时自动生成
     * @param timePosition 截取时间位置（秒），为null时自动选择视频中间位置
     * @param scale 缩放比例，1.0表示原始大小
     * @return 截图文件路径，失败返回null
     */
    public String captureFrame(String videoPath, String outputPath, Double timePosition, double scale) {
        try {
            // 验证输入文件
            File videoFile = new File(videoPath);
            if (!videoFile.exists() || !videoFile.isFile()) {
                log.error("视频文件不存在: {}", videoPath);
                return null;
            }

            // 生成输出路径
            if (outputPath == null) {
                outputPath = generateOutputPath(videoPath);
            }

            // 确保输出目录存在
            File outputFile = new File(outputPath);
            File outputDir = outputFile.getParentFile();
            if (!outputDir.exists()) {
                outputDir.mkdirs();
            }

            // 获取视频时长，用于计算截取位置
            if (timePosition == null) {
                Double duration = getVideoDuration(videoPath);
                if (duration != null && duration > 0) {
                    // 选择视频中间位置，但不超过30秒（避免片头片尾）
                    timePosition = Math.min(duration / 2, 30.0);
                } else {
                    timePosition = 1.0; // 默认第1秒
                }
            }

            // 构建FFmpeg命令
            List<String> command = buildCaptureCommand(videoPath, outputPath, timePosition, scale);
            
            log.info("执行FFmpeg截图命令: {}", String.join(" ", command));

            // 执行命令
            ProcessBuilder pb = new ProcessBuilder(command);
            pb.redirectErrorStream(true);
            Process process = pb.start();

            // 读取输出
            StringBuilder output = new StringBuilder();
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    output.append(line).append("\n");
                }
            }

            // 等待命令执行完成
            boolean finished = process.waitFor(30, TimeUnit.SECONDS);
            if (!finished) {
                process.destroyForcibly();
                log.error("FFmpeg命令执行超时: {}", videoPath);
                return null;
            }

            int exitCode = process.exitValue();
            if (exitCode == 0) {
                // 验证输出文件是否生成
                if (new File(outputPath).exists()) {
                    log.info("视频截图成功: {} -> {}", videoPath, outputPath);
                    return outputPath;
                } else {
                    log.error("FFmpeg执行成功但未生成输出文件: {}", outputPath);
                    return null;
                }
            } else {
                log.error("FFmpeg命令执行失败，退出码: {}, 输出: {}", exitCode, output.toString());
                return null;
            }

        } catch (Exception e) {
            log.error("视频截图异常: videoPath={}", videoPath, e);
            return null;
        }
    }

    /**
     * 获取视频时长（秒）
     * 
     * @param videoPath 视频文件路径
     * @return 视频时长，失败返回null
     */
    public Double getVideoDuration(String videoPath) {
        try {
            List<String> command = new ArrayList<>();
            command.add(ffmpegBin);
            command.add("-i");
            command.add(videoPath);
            command.add("-f");
            command.add("null");
            command.add("-");

            ProcessBuilder pb = new ProcessBuilder(command);
            pb.redirectErrorStream(true);
            Process process = pb.start();

            StringBuilder output = new StringBuilder();
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    output.append(line).append("\n");
                    // 查找时长信息
                    if (line.contains("Duration:")) {
                        String duration = extractDuration(line);
                        if (duration != null) {
                            return parseDurationToSeconds(duration);
                        }
                    }
                }
            }

            process.waitFor(10, TimeUnit.SECONDS);
            return null;

        } catch (Exception e) {
            log.error("获取视频时长失败: {}", videoPath, e);
            return null;
        }
    }

    /**
     * 构建FFmpeg截图命令（支持指定分辨率和质量）
     */
    private List<String> buildCaptureCommandWithResolution(String videoPath, String outputPath, double timePosition, Integer width, Integer height, Integer quality) {
        List<String> command = new ArrayList<>();
        command.add(ffmpegBin);
        
        // 输入文件
        command.add("-i");
        command.add(videoPath);
        
        // 截取时间位置
        command.add("-ss");
        command.add(String.format("%.2f", timePosition));
        
        // 只截取一帧
        command.add("-vframes");
        command.add("1");
        
        // 分辨率设置
        if (width != null && height != null) {
            command.add("-vf");
            command.add(String.format("scale=%d:%d", width, height));
        } else if (width != null) {
            // 只指定宽度，高度按比例缩放
            command.add("-vf");
            command.add(String.format("scale=%d:-1", width));
        } else if (height != null) {
            // 只指定高度，宽度按比例缩放
            command.add("-vf");
            command.add(String.format("scale=-1:%d", height));
        }
        
        // 输出质量设置
        command.add("-q:v");
        command.add(String.valueOf(quality != null ? quality : 2));
        
        // 覆盖输出文件
        command.add("-y");
        
        // 输出文件
        command.add(outputPath);
        
        return command;
    }

    /**
     * 构建FFmpeg截图命令
     */
    private List<String> buildCaptureCommand(String videoPath, String outputPath, double timePosition, double scale) {
        List<String> command = new ArrayList<>();
        command.add(ffmpegBin);
        
        // 输入文件
        command.add("-i");
        command.add(videoPath);
        
        // 截取时间位置
        command.add("-ss");
        command.add(String.format("%.2f", timePosition));
        
        // 只截取一帧
        command.add("-vframes");
        command.add("1");
        
        // 缩放设置
        if (scale != 1.0) {
            command.add("-vf");
            command.add(String.format("scale=iw*%.2f:ih*%.2f", scale, scale));
        }
        
        // 输出质量设置
        command.add("-q:v");
        command.add("2"); // 高质量
        
        // 覆盖输出文件
        command.add("-y");
        
        // 输出文件
        command.add(outputPath);
        
        return command;
    }

    /**
     * 生成输出文件路径
     */
    private String generateOutputPath(String videoPath) {
        try {
            // 创建输出目录
            Path outputDir = Paths.get(defaultOutputDir);
            if (!Files.exists(outputDir)) {
                Files.createDirectories(outputDir);
            }

            // 生成文件名
            String videoFileName = Paths.get(videoPath).getFileName().toString();
            String nameWithoutExt = videoFileName.substring(0, videoFileName.lastIndexOf('.'));
            String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"));
            String outputFileName = String.format("%s_%s.jpg", nameWithoutExt, timestamp);

            return outputDir.resolve(outputFileName).toString();
        } catch (IOException e) {
            log.error("生成输出路径失败", e);
            // 使用临时目录作为备选
            String tempDir = System.getProperty("java.io.tmpdir");
            String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"));
            return Paths.get(tempDir, "video_thumbnail_" + timestamp + ".jpg").toString();
        }
    }

    /**
     * 从FFmpeg输出中提取时长信息
     */
    private String extractDuration(String line) {
        try {
            int start = line.indexOf("Duration: ") + 10;
            int end = line.indexOf(",", start);
            if (start > 9 && end > start) {
                return line.substring(start, end).trim();
            }
        } catch (Exception e) {
            log.debug("提取时长信息失败: {}", line);
        }
        return null;
    }

    /**
     * 将时长字符串转换为秒数
     * 格式: HH:MM:SS.mmm
     */
    private Double parseDurationToSeconds(String duration) {
        try {
            String[] parts = duration.split(":");
            if (parts.length == 3) {
                double hours = Double.parseDouble(parts[0]);
                double minutes = Double.parseDouble(parts[1]);
                double seconds = Double.parseDouble(parts[2]);
                return hours * 3600 + minutes * 60 + seconds;
            }
        } catch (Exception e) {
            log.debug("解析时长失败: {}", duration);
        }
        return null;
    }

    /**
     * 检查FFmpeg是否可用
     */
    public boolean isFFmpegAvailable() {
        try {
            ProcessBuilder pb = new ProcessBuilder(ffmpegBin, "-version");
            Process process = pb.start();
            boolean finished = process.waitFor(5, TimeUnit.SECONDS);
            if (finished && process.exitValue() == 0) {
                log.info("FFmpeg可用: {}", ffmpegBin);
                return true;
            }
        } catch (Exception e) {
            log.warn("FFmpeg不可用: {}", ffmpegBin, e);
        }
        return false;
    }
}
